#include "global.h"
#include "battle.h"
#include "battle_evolution.h"
#include "decompress.h"
#include "item.h"
#include "constants/hold_effects.h"
#include "battle_interface.h"
#include "task.h"
#include "battle_anim.h"
#include "constants/items.h"
#include "battle_scripts.h"
#include "constants/species.h"
#include "constants/moves.h"
#include "constants/species.h"

#define tBattler    data[0]
#define tHide       data[1]

#define SINGLES_MEGA_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_MEGA_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_MEGA_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_MEGA_TRIGGER_POS_Y_DIFF (-11)

#define DOUBLES_MEGA_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_MEGA_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_MEGA_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_MEGA_TRIGGER_POS_Y_DIFF (-4)

void GetBattlerHealthboxCoords(u8 battler, s16 *x, s16 *y);
extern u8 gZMovePower[];
#define GFX_TAG_DYNAMAX_INDICATOR TAG_MEGA_INDICATOR_TILE+2

void SpriteCb_MegaIndicator(struct Sprite* sprite);
static const u32 Dynamax_IndicatorTiles[] = INCBIN_U32("graphics/battle_interface/Dynamax_Indicator.4bpp");
static const u16 Dynamax_IndicatorPal[] = INCBIN_U16("graphics/battle_interface/Dynamax_Indicator.gbapal");
//extern const u32 Dynamax_TriggerTiles[]; //For some reason this doesn't work
static const u32 Dynamax_Trigger_WorkingTiles[] = INCBIN_U32("graphics/battle_interface/dynamax_trigger.4bpp.lz"); //This is used as the image until the bug is fixed
static const u16 Dynamax_TriggerPal[] = INCBIN_U16("graphics/battle_interface/dynamax_trigger.gbapal");
#define GET_BATTLER_SIDE3(battler) (battler&1)
bool32 CanEvolve(u32 species);
static u16 GetFeverWishSpecies(u32 battlerId);

const struct SpriteSheet sDynamaxIndicatorSpriteSheet =
{
	Dynamax_IndicatorTiles, (8 * 8) / 2, GFX_TAG_DYNAMAX_INDICATOR
};
const struct SpritePalette sDynamaxIndicatorPalette =
{
	Dynamax_IndicatorPal, GFX_TAG_DYNAMAX_INDICATOR
};
static const struct OamData sIndicatorOam =
{
	.affineMode = ST_OAM_AFFINE_OFF,
	.objMode = ST_OAM_OBJ_NORMAL,
	.shape = SPRITE_SHAPE(8x8),
	.size = SPRITE_SIZE(8x8),
	.priority = 0, //Above sprites
};
static const struct SpriteTemplate sDynamaxIndicatorSpriteTemplate =
{
	.tileTag = GFX_TAG_DYNAMAX_INDICATOR,
	.paletteTag = GFX_TAG_DYNAMAX_INDICATOR,
	.oam = &sIndicatorOam,
	.anims = gDummySpriteAnimTable,
	.images = NULL,
	.affineAnims = gDummySpriteAffineAnimTable,
	.callback = SpriteCb_MegaIndicator,
};

static const u8 sMegaTriggerGfx[] = INCBIN_U8("graphics/battle_interface/mega_trigger.4bpp.lz");
static const u16 sMegaTriggerPal[] = INCBIN_U16("graphics/battle_interface/mega_trigger.gbapal");
static const u8 sZTriggerGfx[] = INCBIN_U8("graphics/battle_interface/z_trigger.4bpp.lz");
static const u16 sZTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_trigger.gbapal");


struct SpriteImgPalSheet
{
	const void* sprite;
	const void* pal;
};

const struct SpriteImgPalSheet gBattleTriggers[] =
{
	{sMegaTriggerGfx, sMegaTriggerPal},
	{Dynamax_Trigger_WorkingTiles, Dynamax_TriggerPal},
	{sZTriggerGfx, sZTriggerPal},
};

extern const struct SpriteTemplate sSpriteTemplate_MegaTrigger;
#define SHAPE_SIZE(width, height) (width*height/2)
void CreateTriggerSprite(u8 battlerId, u8 type)
{
	struct CompressedSpriteSheet trigger = {gBattleTriggers[type].sprite, SHAPE_SIZE(32, 64), TAG_MEGA_TRIGGER_TILE};
	struct SpritePalette palette = {gBattleTriggers[type].pal,TAG_MEGA_TRIGGER_TILE};
 	LoadSpritePalette(&palette);
    if (GetSpriteTileStartByTag(TAG_MEGA_TRIGGER_TILE) == 0xFFFF)
        LoadCompressedSpriteSheet(&trigger);
    if (gBattleStruct->mega.triggerSpriteId != 0xFF)
        DestroySpriteAndFreeResources(&gSprites[gBattleStruct->mega.triggerSpriteId]);
    if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
            gBattleStruct->mega.triggerSpriteId = CreateSprite(&sSpriteTemplate_MegaTrigger,
                                                             gSprites[gHealthboxSpriteIds[battlerId]].pos1.x - DOUBLES_MEGA_TRIGGER_POS_X_SLIDE,
                                                             gSprites[gHealthboxSpriteIds[battlerId]].pos1.y - DOUBLES_MEGA_TRIGGER_POS_Y_DIFF, 0);
        else
            gBattleStruct->mega.triggerSpriteId = CreateSprite(&sSpriteTemplate_MegaTrigger,
                                                             gSprites[gHealthboxSpriteIds[battlerId]].pos1.x - SINGLES_MEGA_TRIGGER_POS_X_SLIDE,
                                                             gSprites[gHealthboxSpriteIds[battlerId]].pos1.y - SINGLES_MEGA_TRIGGER_POS_Y_DIFF, 0);
    gSprites[gBattleStruct->mega.triggerSpriteId].tBattler = battlerId;
    gSprites[gBattleStruct->mega.triggerSpriteId].tHide = FALSE;

}

u32 CreateDynamaxIndicator(u32 battlerId)
{
    u32 spriteId, position;
    LoadSpritePalette(&sDynamaxIndicatorPalette);
    LoadSpriteSheet(&sDynamaxIndicatorSpriteSheet);
    spriteId = CreateSprite(&sDynamaxIndicatorSpriteTemplate, 0, 0, 1);
    gSprites[gSprites[gHealthboxSpriteIds[battlerId]].oam.affineParam].data[6] = spriteId;
    
    gSprites[spriteId].tBattler = battlerId;
    gSprites[spriteId].data[2] = 5;
    return spriteId;
}

void SetEvolutionType(u8 battlerId, enum BattleEvolutionType value)
{
	gBattleStruct->mega.evolutionType[GET_BATTLER_SIDE3(battlerId)][gBattlerPartyIndexes[battlerId]] = value;
}

enum BattleEvolutionType GetEvolutionType(u8 battlerId)
{
    return gBattleStruct->mega.evolutionType[GET_BATTLER_SIDE3(battlerId)][gBattlerPartyIndexes[battlerId]];
}


static bool32 IsPartnerMonFromSameTrainer(u8 battlerId)
{
    if (GET_BATTLER_SIDE3(battlerId) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
        return FALSE;
    else if (GET_BATTLER_SIDE3(battlerId) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
        return FALSE;
    else if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
        return FALSE;
    else
        return TRUE;
}




bool32 IsEvolutionHappened(u32 battlerId, u8 type)
{
    u8 partnerPosition = BATTLE_PARTNER(battlerId);
    if (gBattleStruct->mega.alreadyEvolved[(battlerId)] & (1 << type))
        return 1;
    if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) {//队友已经使用过，并且类型是一样的
        if (IsPartnerMonFromSameTrainer(battlerId)
            && ((gBattleStruct->mega.alreadyEvolved[partnerPosition] & (1 << type))
                || (gBattleStruct->mega.toEvolve & (1 << partnerPosition)))
            && GetEvolutionType(battlerId) == type)
            return 1;
    }
    return 0;
}

bool8 DisableBattlerEvolution(u8 battlerId, u8 monId,enum BattleEvolutionType type)
{
    if (gBattleStruct->mega.partyEvolvedType[GET_BATTLER_SIDE3(battlerId)][monId] == 1 &&
        gBattleStruct->mega.evolutionType[GET_BATTLER_SIDE3(battlerId)][monId] == type)
    {
        gBattleStruct->mega.partyEvolvedType[GET_BATTLER_SIDE3(battlerId)][monId] = 2;
        return 1;
    }
    return 0;
}

u8 GetBattlerInMegaOrDynamax(u8 battlerId)
{
    u8 type = CurrentBttlerEvolutionStat(battlerId);
    return (type == EvolutionMega || type == EvolutionDynamax);
}

enum BattleEvolutionType CurrentBttlerEvolutionStat(u8 battlerId)
{
    u8 status = gBattleStruct->mega.partyEvolvedType[GET_BATTLER_SIDE3(battlerId)][gBattlerPartyIndexes[battlerId]];
    if (status == 1)
        return gBattleStruct->mega.evolutionType[GET_BATTLER_SIDE3(battlerId)][gBattlerPartyIndexes[battlerId]];
    return 0;
}



void SetEvolutionHappend(u8 battlerId)
{
    gBattleStruct->mega.partyEvolvedType[GET_BATTLER_SIDE3(battlerId)][gBattlerPartyIndexes[battlerId]] = 1;
    gBattleStruct->mega.alreadyEvolved[battlerId] |= 1 << GetEvolutionType(battlerId);
}

static bool8 PlayerHasItem(u16 item, u8 battlerId)
{
    if (USE_BATTLE_DEBUG && GET_BATTLER_SIDE3(battlerId) != B_SIDE_PLAYER)
        return TRUE;
    return CheckBagHasItem(item, 1);
}

static bool32 CanPokemonMega(u8 battlerId)
{
	u16 itemId;
    u16 species;
    if (IsEvolutionHappened(battlerId,EvolutionMega))
        return FALSE;

    itemId = gBattleMons[battlerId].item;
    if (!PlayerHasItem(ITEM_MEGA_BRACELET, battlerId))
        return FALSE;
    if ((species = GetMegaEvolutionSpecies(battlerId, itemId)) == 0)
		return FALSE;
    if (species == SPECIES_MEGA_RAYQUAZA)
        return TRUE;
	if (ItemId_GetHoldEffect(itemId) != HOLD_EFFECT_MEGA_STONE){
		return FALSE;
	}
    if (FindBattlerEvolution(battlerId, EVO_CANNOT_DYNAMAX) != 0)
        return FALSE;
	// All checks passed, the mon CAN mega evolve.
	return TRUE;
}


static bool32 CanPokemonDynamax(u8 battlerId)
{
    u16 itemEffect;
    /*if (!(gBattleTypeFlags & BATTLE_TYPE_DYNAMAX))
        return FALSE;*/
    if (IsEvolutionHappened(battlerId,EvolutionDynamax))
        return FALSE;
    itemEffect = ItemId_GetHoldEffect(gBattleMons[battlerId].item);
    if (itemEffect == HOLD_EFFECT_Z_CRYSTAL || itemEffect == HOLD_EFFECT_MEGA_STONE)
        return FALSE;
    if (CanPokemonMega(battlerId))
        return FALSE;
    if (FindBattlerEvolution(gBattleMons[battlerId].species, EVO_CANNOT_DYNAMAX) != 0)
        return FALSE;
    if (!PlayerHasItem(ITEM_DYNAMAX_BAND, battlerId))
        return FALSE;
    if (GET_BATTLER_SIDE3(battlerId) == B_SIDE_OPPONENT && ((gOppenentDyanamaxFlag & (1 << gBattlerPartyIndexes[battlerId])) == 0))
        return FALSE;
    return TRUE;
}

const union AffineAnimCmd sDynamaxGrowthAffineAnimCmds[] =
{
    AFFINEANIMCMD_FRAME(-2, -2, 0, 64), //Double in size
    AFFINEANIMCMD_FRAME(0, 0, 0, 64),
    AFFINEANIMCMD_FRAME(2, 2, 0, 64),
    AFFINEANIMCMD_END,
};

const union AffineAnimCmd sDynamaxGrowthAttackAnimationAffineAnimCmds[] =
{
    AFFINEANIMCMD_FRAME(-4, -4, 0, 32), //Double in size quicker
    AFFINEANIMCMD_FRAME(0, 0, 0, 32), //Pause for less
    AFFINEANIMCMD_FRAME(16, 16, 0, 8),
    AFFINEANIMCMD_END,
};

const struct Evolution* RevertEvoBaseSpecies(struct Pokemon* poke, u16 type) {
    u16 species = GetMonData(poke, MON_DATA_SPECIES);
    u8 i = 0;
    for (; i < EVOS_PER_MON; i++)
    {
        if (gEvolutionTable[species][i].method == type)
        {
            SetMonData2(poke, MON_DATA_SPECIES, gEvolutionTable[species][i].targetSpecies);
            CalculateMonStats(poke);
            return &gEvolutionTable[species][i];
        }
    }
    
    return 0;
}

u16 GetGMaxSpeciesInfo(u16 species)
{
    const struct Evolution* evolution = FindBattlerEvolution(species, EVO_DYNAMAX);
    if (evolution == 0)
        return 0;
    return evolution->targetSpecies;
}

void GetMonMoves(struct Pokemon* mon, u16* moves)
{
	u8 i = 0;
	for (; i < 4; i++)
	{
		moves[i] = GetMonData(mon, MON_DATA_MOVE1 + i);
	}
}

void RestoreMaxMove(u32 battlerId)
{
    GetMonMoves(GetBankPartyData(battlerId), gBattleMons[battlerId].moves);
}

u8 GetDynamaxHPBoost(u8 battler)
{
    return 2;
}

bool32 DynamaxUpdateEndturn(u32 battlerId)
{
	u8* timer = &gBattleStruct->mega.timer[gActiveBattler];
	struct Pokemon* pokemon;
	if (*timer && --(*timer) == 0)
	{
        DisableBattlerEvolution(battlerId, gBattlerPartyIndexes[battlerId], EvolutionDynamax);
		pokemon = GetBankPartyData(battlerId);
		pokemon->hp = gBattleMons[battlerId].hp /= GetDynamaxHPBoost(battlerId);
		pokemon->maxHP = gBattleMons[battlerId].maxHP /= GetDynamaxHPBoost(battlerId);
		gBattleStruct->choicedMove[battlerId] = 0;//解除讲究眼镜
		gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies = 0;
        gBattleSpritesDataPtr->battlerData[battlerId].dynamax = 0;
		RestoreMaxMove(battlerId);
		DestroyMegaIndicatorSprite(gHealthboxSpriteIds[battlerId]);
		BattleScriptExecute(BattleScript_Dynamax_End);
		return TRUE;
	}
    return FALSE;
}

// Functions
bool8 IsZMove(u16 move)
{
    return move >= MOVE_BREAKNECK_BLITZ && move <= MOVE_SOUL_STEALING_7_STAR_STRIKE;
}

struct SignatureZMove
{
    u16 species;
    u16 item;
    u16 move;
    u16 zmove;
};



static const struct SignatureZMove sSignatureZMoves[] =
{
    {SPECIES_ALOLAN_RAICHU,         ITEM_ALORAICHIUM_Z,        MOVE_THUNDERBOLT,         MOVE_STOKED_SPARKSURFER},
    {SPECIES_DECIDUEYE,             ITEM_DECIDIUM_Z,           MOVE_SPIRIT_SHACKLE,      MOVE_SINISTER_ARROW_RAID},
    {SPECIES_INCINEROAR,            ITEM_INCINIUM_Z,           MOVE_DARKEST_LARIAT,      MOVE_MALICIOUS_MOONSAULT},
    {SPECIES_KOMMO_O,               ITEM_KOMMONIUM_Z,          MOVE_CLANGING_SCALES,     MOVE_CLANGOROUS_SOULBLAZE},
    {SPECIES_LUNALA,                ITEM_LUNALIUM_Z,           MOVE_MOONGEIST_BEAM,      MOVE_MENACING_MOONRAZE_MAELSTROM},
//    {SPECIES_NECROZMA_DAWN_WINGS,   ITEM_LUNALIUM_Z,           MOVE_MOONGEIST_BEAM,      MOVE_MENACING_MOONRAZE_MAELSTROM},
    {SPECIES_LYCANROC,              ITEM_LYCANIUM_Z,           MOVE_STONE_EDGE,          MOVE_SPLINTERED_STORMSHARDS},
    {SPECIES_LYCANROC_MIDNIGHT,     ITEM_LYCANIUM_Z,           MOVE_STONE_EDGE,          MOVE_SPLINTERED_STORMSHARDS},

    {SPECIES_LYCANROC_DUSK,         ITEM_LYCANIUM_Z,           MOVE_STONE_EDGE,          MOVE_SPLINTERED_STORMSHARDS},
    {SPECIES_MARSHADOW,             ITEM_MARSHADIUM_Z,         MOVE_SPECTRAL_THIEF,      MOVE_SOUL_STEALING_7_STAR_STRIKE},
    {SPECIES_MIMIKYU,               ITEM_MIMIKIUM_Z,           MOVE_PLAY_ROUGH,          MOVE_LETS_SNUGGLE_FOREVER},
    {SPECIES_MIMIKYU_BUSTED,        ITEM_MIMIKIUM_Z,           MOVE_PLAY_ROUGH,          MOVE_LETS_SNUGGLE_FOREVER},
   /* {SPECIES_PIKACHU_ORIGINAL_CAP,  ITEM_PIKASHUNIUM_Z,        MOVE_THUNDERBOLT,         MOVE_10000000_VOLT_THUNDERBOLT},
    {SPECIES_PIKACHU_HOENN_CAP,     ITEM_PIKASHUNIUM_Z,        MOVE_THUNDERBOLT,         MOVE_10000000_VOLT_THUNDERBOLT},
    {SPECIES_PIKACHU_SINNOH_CAP,    ITEM_PIKASHUNIUM_Z,        MOVE_THUNDERBOLT,         MOVE_10000000_VOLT_THUNDERBOLT},
    {SPECIES_PIKACHU_UNOVA_CAP,     ITEM_PIKASHUNIUM_Z,        MOVE_THUNDERBOLT,         MOVE_10000000_VOLT_THUNDERBOLT},
    {SPECIES_PIKACHU_KALOS_CAP,     ITEM_PIKASHUNIUM_Z,        MOVE_THUNDERBOLT,         MOVE_10000000_VOLT_THUNDERBOLT},
    {SPECIES_PIKACHU_ALOLA_CAP,     ITEM_PIKASHUNIUM_Z,        MOVE_THUNDERBOLT,         MOVE_10000000_VOLT_THUNDERBOLT},
    {SPECIES_PIKACHU_PARTNER_CAP,   ITEM_PIKASHUNIUM_Z,        MOVE_THUNDERBOLT,         MOVE_10000000_VOLT_THUNDERBOLT},*/
    {SPECIES_PRIMARINA,             ITEM_PRIMARIUM_Z,          MOVE_SPARKLING_ARIA,      MOVE_OCEANIC_OPERETTA},
    {SPECIES_SOLGALEO,              ITEM_SOLGANIUM_Z,          MOVE_SUNSTEEL_STRIKE,     MOVE_SEARING_SUNRAZE_SMASH},
//    {SPECIES_NECROZMA_DUSK_MANE,    ITEM_SOLGANIUM_Z,          MOVE_SUNSTEEL_STRIKE,     MOVE_SEARING_SUNRAZE_SMASH},
    {SPECIES_TAPU_KOKO,             ITEM_TAPUNIUM_Z,           MOVE_NATURES_MADNESS,     MOVE_GUARDIAN_OF_ALOLA},
    {SPECIES_TAPU_BULU,             ITEM_TAPUNIUM_Z,           MOVE_NATURES_MADNESS,     MOVE_GUARDIAN_OF_ALOLA},
    {SPECIES_TAPU_LELE,             ITEM_TAPUNIUM_Z,           MOVE_NATURES_MADNESS,     MOVE_GUARDIAN_OF_ALOLA},
    {SPECIES_TAPU_FINI,             ITEM_TAPUNIUM_Z,           MOVE_NATURES_MADNESS,     MOVE_GUARDIAN_OF_ALOLA},
//    {SPECIES_NECROZMA_ULTRA,        ITEM_ULTRANECROZIUM_Z,     MOVE_PHOTON_GEYSER,       MOVE_LIGHT_THAT_BURNS_THE_SKY},
    {SPECIES_MEW,                   ITEM_MEWNIUM_Z,            MOVE_PSYCHIC,             MOVE_GENESIS_SUPERNOVA},
    {SPECIES_PIKACHU,               ITEM_PIKANIUM_Z,           MOVE_VOLT_TACKLE,         MOVE_CATASTROPIKA},
    {SPECIES_EEVEE,                 ITEM_EEVIUM_Z,             MOVE_LAST_RESORT,         MOVE_EXTREME_EVOBOOST},
    {SPECIES_SNORLAX,               ITEM_SNORLIUM_Z,           MOVE_GIGA_IMPACT,         MOVE_PULVERIZING_PANCAKE},
};

u16 GetSignatureZMove(u16 move, u16 species, u16 item)
{
    u32 i;

    // check signature z move
    for (i = 0; i < ARRAY_COUNT(sSignatureZMoves); ++i)
    {
        if (sSignatureZMoves[i].item == item && sSignatureZMoves[i].species == species &&  sSignatureZMoves[i].move == move)
            return sSignatureZMoves[i].zmove;
    }

    return MOVE_NONE;
}


bool8 IsSpecialZCrystal(u16 item)
{
    u32 i;
    for (i = 0; i < ARRAY_COUNT(sSignatureZMoves); ++i)
    {
        if (sSignatureZMoves[i].item == item)
            return TRUE;
    }

    return FALSE;
}

static u16 GetTypeBasedZMove(u16 move, u8 battler)
{
    u8 moveType = gBattleMoves[move].type;

    // get z move from type
    if (moveType < TYPE_FIRE)
        return MOVE_BREAKNECK_BLITZ + moveType;
    else if (moveType >= TYPE_FAIRY)
        return MOVE_TWINKLE_TACKLE + (moveType - TYPE_FAIRY);
    else
        return MOVE_BREAKNECK_BLITZ + (moveType - 1);
}

u16 ConvertZMove(u16 move, u16 species, u16 itemId)
{
	u16 zMove = GetSignatureZMove(move, species, itemId);
    if (zMove != MOVE_NONE)
    {
        return zMove;
    }

    if (move != MOVE_NONE && gBattleMoves[move].type == ItemId_GetSecondaryId(itemId))
    {
        if (gBattleMoves[move].split == SPLIT_STATUS)
            return move;
        else
            return GetTypeBasedZMove(move, gActiveBattler);
    }
    return 0;
}
extern void HandleInputChooseMove();

bool32 CanUseZMove(u8 battlerId)
{
    u16 itemId ,i, speices;
    if (gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FRONTIER))
        return FALSE;
    if (IsEvolutionHappened(battlerId,EvolutionZmove))
        return FALSE;
    if (!PlayerHasItem(ITEM_Z_RING, battlerId))
        return FALSE;
    itemId = gBattleMons[battlerId].item;

    if (ItemId_GetHoldEffect(itemId) != HOLD_EFFECT_Z_CRYSTAL)
    {
        return FALSE;
    }
    speices = gBattleMons[battlerId].species;
    if (gBattleStruct->mega.triggerSpriteId == 0xFF) //trigger created, now active z move
	{
    	for(i = 0;i < 4; i++)
		{
			if (ConvertZMove(gBattleMons[battlerId].moves[i], speices, itemId))
			{
				return TRUE;
			}
		}
	}
    else
	{
    	if (ConvertZMove(gBattleMons[battlerId].moves[gMoveSelectionCursor[gActiveBattler]], speices, itemId))
		{
    		return TRUE;
		}
	}
    return FALSE;
}

u8 GetZMovePower(u8 battlerId)
{
    return gZMovePower[gBattleStruct->mega.choosenZmove[battlerId]];
}

void DoZMove(u32 battlerId)
{
    u16* move = &gBattleMons[gActiveBattler].moves[gMoveSelectionCursor[gActiveBattler]];
    if (!CanUseZMove(battlerId))//double check
        return;
    gBattleStruct->mega.choosenZmove[gActiveBattler] = *move;//store it
    *move = ConvertZMove(*move, gBattleMons[gActiveBattler].species, gBattleMons->item);
}

void ResetCurrentZMove(u32 battlerId)
{
    u16* move = &gBattleMons[battlerId].moves[gMoveSelectionCursor[battlerId]];
    *move = gBattleStruct->mega.choosenZmove[battlerId];//restore
    //choosenZmove仍然保留，可以为后面的获取zmove power做准备
}

void AttackCancelerZMove()
{
	if (gBattleStruct->mega.choosenZmove[gBattlerAttacker] == 0)
		return;
    ResetCurrentZMove(gBattlerAttacker);
    SetEvolutionHappend(gBattlerAttacker);
    BattleScriptPushCurrent(BattleScript_ZMoveActivateDamaging);//execute z move script
}

#include "constants/battle_move_effects.h"

#define Z_EFFECT_BS_LENGTH 0
#define MULTISTRING_Z_RESET_STATS   0
#define MULTISTRING_Z_ALL_STATS_UP  1
#define MULTISTRING_Z_BOOST_CRITS   2
#define MULTISTRING_Z_FOLLOW_ME     3
#define MULTISTRING_Z_RECOVER_HP    4
#define MULTISTRING_Z_STAT_UP       5
#define MULTISTRING_Z_HP_TRAP       6

bool32 AreStatsMaxed(u8 battlerId, u8 n)
{
    u32 i;
    for (i = STAT_ATK; i <= n; i++)
    {
        if (gBattleMons[battlerId].statStages[i - 1] < MAX_STAT_STAGE)
            return FALSE;
    }
    return TRUE;
}

void SetZEffect(void)
{
    u32 i,effect;
    effect = GetZMovePower(gBattleAnimAttacker);
    if (effect == Z_EFFECT_CURSE)
    {
        if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
            effect = Z_EFFECT_RECOVER_HP;
        else
            effect = Z_EFFECT_ATK_UP_1;
    }

    gBattleScripting.battler = gBattlerAttacker;
    switch (effect)
    {
    case Z_EFFECT_RESET_STATS:
        for (i = 0; i < NUM_BATTLE_STATS - 1; i++)
        {
            if (gBattleMons[gBattlerAttacker].statStages[i] < DEFAULT_STAT_STAGE)
                gBattleMons[gBattlerAttacker].statStages[i] = DEFAULT_STAT_STAGE;
        }
        gBattleCommunication[MULTISTRING_CHOOSER] = MULTISTRING_Z_RESET_STATS;
        BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
        gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
        break;
    case Z_EFFECT_ALL_STATS_UP_1:
        if (!AreStatsMaxed(gBattlerAttacker, STAT_SPDEF))
        {
            for (i = 0; i < STAT_ACC - 1; i++) //Doesn't increase Acc or Evsn
            {
                if (gBattleMons[gBattlerAttacker].statStages[i] < 12)
                    ++gBattleMons[gBattlerAttacker].statStages[i];
            }
            gBattleCommunication[MULTISTRING_CHOOSER] = MULTISTRING_Z_ALL_STATS_UP;
            BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
            gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
        }
        break;
    case Z_EFFECT_BOOST_CRITS:
        if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY))
        {
            gBattleMons[gBattlerAttacker].status2 |= STATUS2_FOCUS_ENERGY;
            gBattleCommunication[MULTISTRING_CHOOSER] = MULTISTRING_Z_BOOST_CRITS;
            BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
            gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
        }
        break;
    case Z_EFFECT_FOLLOW_ME:
        gSideTimers[GetBattlerSide(gBattlerAttacker)].followmeTimer = 1;
        gSideTimers[GetBattlerSide(gBattlerAttacker)].followmeTarget = gBattlerAttacker;
        gBattleCommunication[MULTISTRING_CHOOSER] = MULTISTRING_Z_FOLLOW_ME;
        BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
        gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
        break;
    case Z_EFFECT_RECOVER_HP:
        if (gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP)
        {
            gBattleMoveDamage = (-1) * gBattleMons[gBattlerAttacker].maxHP;
            gBattleCommunication[MULTISTRING_CHOOSER] = MULTISTRING_Z_RECOVER_HP;
            BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
            gBattlescriptCurrInstr = BattleScript_RecoverHPZMove;
        }
        break;
    case Z_EFFECT_RESTORE_REPLACEMENT_HP:
//        gBattleStruct->zmove.healReplacement = TRUE;
        BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
        gBattleCommunication[MULTISTRING_CHOOSER] = MULTISTRING_Z_HP_TRAP;
        gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
        break;
    case Z_EFFECT_ATK_UP_1:
    case Z_EFFECT_DEF_UP_1:
    case Z_EFFECT_SPD_UP_1:
    case Z_EFFECT_SPATK_UP_1:
    case Z_EFFECT_SPDEF_UP_1:
    case Z_EFFECT_ACC_UP_1:
    case Z_EFFECT_EVSN_UP_1:
        SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_1 + 1, 1, FALSE);
        gBattleScripting.animArg1 = 0xE + (effect - Z_EFFECT_ATK_UP_1 + 1);
        gBattleScripting.animArg2 = 0;
        BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
        gBattlescriptCurrInstr = BattleScript_StatUpZMove;
        break;
    case Z_EFFECT_ATK_UP_2:
    case Z_EFFECT_DEF_UP_2:
    case Z_EFFECT_SPD_UP_2:
    case Z_EFFECT_SPATK_UP_2:
    case Z_EFFECT_SPDEF_UP_2:
    case Z_EFFECT_ACC_UP_2:
    case Z_EFFECT_EVSN_UP_2:
        SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_2 + 1, 2, FALSE);
        gBattleScripting.animArg1 = 0xE + (effect - Z_EFFECT_ATK_UP_2 + 1);
        gBattleScripting.animArg2 = 0;
        BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
        gBattlescriptCurrInstr = BattleScript_StatUpZMove;
        break;
    case Z_EFFECT_ATK_UP_3:
    case Z_EFFECT_DEF_UP_3:
    case Z_EFFECT_SPD_UP_3:
    case Z_EFFECT_SPATK_UP_3:
    case Z_EFFECT_SPDEF_UP_3:
    case Z_EFFECT_ACC_UP_3:
    case Z_EFFECT_EVSN_UP_3:
        SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_3 + 1, 3, FALSE);
        gBattleScripting.animArg1 = 0xE + (effect - Z_EFFECT_ATK_UP_3 + 1);
        gBattleScripting.animArg2 = 0;
        BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
        gBattlescriptCurrInstr = BattleScript_StatUpZMove;
        break;
    default:
        gBattlescriptCurrInstr += Z_EFFECT_BS_LENGTH;
        break;
    }
}


struct Pokemon* GetBankPartyData(u8 bank)
{
    u8 index = gBattlerPartyIndexes[bank];
    return (GET_BATTLER_SIDE3(bank) == B_SIDE_OPPONENT) ? &gEnemyParty[index] : &gPlayerParty[index];
}


//预先计算是否可以进行形态变化，如果计算成功，则后续不会重复计算
//如果计算不成功，后面还会重新计算。
enum BattleEvolutionType CalEvolutionType(u8 battlerId)
{
    struct Pokemon* pokemon = GetBankPartyData(battlerId);
    if (CanPokemonMega(battlerId))
        return EvolutionMega;
    if (CanPokemonDynamax(battlerId))
        return EvolutionDynamax;
    if (CanUseZMove(battlerId))
        return EvolutionZmove;
    return EvolutionNone;
}


static const struct SpriteTemplate sPokemonSpriteTemplate =
{
    .tileTag = 9527,
    .paletteTag = 9527,
    .oam = &gOamData_831ACA8,
    .anims = gDummySpriteAnimTable,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCallbackDummy,
};

u8 AnimTaskSwapDynamaxSpriteCopySprite()
{
    struct CompressedSpritePalette palette;
    struct SpriteSheet spriteSheet;
    struct Sprite* poke = &gSprites[GetAnimBattlerSpriteId(gBattleAnimAttacker)];
    palette.tag = sPokemonSpriteTemplate.paletteTag;
    palette.data = GetMonSpritePalStruct(GetBankPartyData(gBattleAnimAttacker))->data;
    LoadCompressedSpritePalette(&palette);
    spriteSheet.tag = palette.tag;
    spriteSheet.data =  gMonSpritesGfxPtr->sprites[GET_BATTLER_POSITION(gBattleAnimAttacker)];
    spriteSheet.size = 0x800;
    LoadSpriteSheet(&spriteSheet);
    return CreateSprite(&sPokemonSpriteTemplate, poke->pos1.x, poke->pos1.y, 0);
}


#include "gpu_regs.h"

void AnimTask_DynamaxGrowthStep(u8 taskId)
{
    struct Task* task = &gTasks[taskId];
    if (!RunAffineAnimFromTaskData(&gTasks[taskId]))
    {
        DestroyAnimVisualTask(taskId);
    }
    if (task->data[4] && task->data[7] == 1)
    {
        SetGpuReg(REG_OFFSET_BLDCNT, 0);
        SetGpuReg(REG_OFFSET_BLDALPHA, 0);
        DestroySpriteAndFreeResources(&gSprites[task->data[3]]);
    }
}

void AnimTaskSwapDynamaxSprite(u8 taskId)
{
    struct Task* task = &gTasks[taskId];
    u8 spriteId = GetAnimBattlerSpriteId(ANIM_ATTACKER);
    u16 species = GetGMaxSpeciesInfo(gBattleMons[gBattleAnimAttacker].species);
    PrepareAffineAnimInTaskData(task, spriteId,
                                gBattleAnimArgs[0] == 1 ?
                                sDynamaxGrowthAttackAnimationAffineAnimCmds:sDynamaxGrowthAffineAnimCmds);
    if (species != 0 && gBattleAnimArgs[0] != 1)
    {
        task->data[3] = AnimTaskSwapDynamaxSpriteCopySprite();
        task->data[4] = TRUE;
        gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies = species;
        SetGpuReg(REG_OFFSET_BLDCNT, (BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_ALL));
        SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(8, 4));
        PrepareBattlerSpriteForRotScale(spriteId, ST_OAM_OBJ_BLEND);
    }
    if (gBattleAnimArgs[0] != 1)
    {
        gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].dynamax = 1;
        LoadBattleMonGfxAndAnimate(gBattleAnimAttacker, 1, spriteId);
    }

    task->func = AnimTask_DynamaxGrowthStep;
}

u8 CalcBestArceusMoveType()
{
    s8 typeArray[NUMBER_OF_MON_TYPES] = {0};
    u8 i,j;
    s8 max = 0;
    u8 max_idx = 0;
    for(j = 0 ; j < 3; j++)
    {
        u8 targetType = GetBattlerType(gBattlerTarget, BATTLE_MON_TYPE_1 + j);
        if (targetType == TYPE_MYSTERY)
            continue;
        for (i = 0; i < NUMBER_OF_MON_TYPES; i++)
        {
            u8 modifier;
            if (i == TYPE_MYSTERY)
                continue;
            modifier = GetTypeModifier(i, targetType);
            if(modifier == 0)
            {
                typeArray[i] = -3;
            }
            else if (GetTypeModifier(i, targetType) > 4096)
            {
                typeArray[i]++;
                 if(typeArray[i] > max)
                 {
                    max = typeArray[i];
                    max_idx = i;
                }
            }
        }
    }
    return max_idx;
}

bool8 TryLegendaryFormChange()
{
    if (gCurrentMove == MOVE_JUDGMENT && GetFormSpeciesId(gBattleMons[gBattlerAttacker].species, 0) == SPECIES_ARCEUS
        && gBattleMons[gBattlerAttacker].item == ITEM_LEGENDARY_PLATE) {
        u8 bestType = CalcBestArceusMoveType();
        if (bestType!= GetBattlerType(gBattlerAttacker, BATTLE_MON_TYPE_1)) {
            if (bestType > TYPE_MYSTERY)
                bestType--;
            gBattleMons[gBattlerAttacker].species = GetFormSpeciesId(SPECIES_ARCEUS, bestType);
            BattleScriptPushCursor();
            gBattlescriptCurrInstr = BattleScript_AttackerFormChange;
            return TRUE;
        }
    }
    return FALSE;
}
